package com.me.myFramework.myAop.aop300;

import com.me.myFramework.myAop.aop300.advice.Advice;
import com.me.myFramework.myAop.aop300.advice.method.AfterReturningAdvice;
import com.me.myFramework.myAop.aop300.advice.method.MethodBeforeAdvice;
import com.me.myFramework.myAop.aop300.intercept.method.*;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.aop.Pointcut;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.List;

/**
 * 自己实现 AOP-300：  <p></p>
 *
 * 用来生成 AOP代理对象的工厂
 *
 * @author ME
 * @date 2021/9/27
 */
public class ProxyFactory implements InvocationHandler {

    /** 目标对象 */
    private final Object target;
    /** 目标类 */
    private final Class<?> targetClass;

    /**
     * 一个完整的切面对象（pointcut + advice）
     */
    private List<PointcutAdvisor> advisors = new ArrayList<>();

    public ProxyFactory(Object target) {
        this.target = target;
        this.targetClass = target.getClass();
    }

    /**
     *
     */
    public Object getProxy(ClassLoader classLoader) {
        return Proxy.newProxyInstance(classLoader, targetClass.getInterfaces(), this);
    }

    /**
     * 改进 AOP-200：
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object retVal;

        // 筛选出当前正在执行的方法适用的 advice（增强），并转换成 拦截器（执行增强逻辑的执行器）
        List<MethodInterceptor> interceptorList = getInterceptors(method, targetClass);
        if (CollectionUtils.isNotEmpty(interceptorList)) {
            // TODO 改进 AOP-200：在那一版中，每个 advice模板方法都需要判断自己是不是最后一个切面，如果不是则继续调用下一个切面。把对应的调用逻辑抽取到 MethodInvocation
            MethodInvocation mi = new ReflectiveMethodInvocation(proxy, target, method, args, interceptorList);
            retVal = mi.proceed();
        }
        else {
            retVal = method.invoke(target, args);
        }

        return retVal;
    }

    /**
     * 筛选出当前正在执行的方法适用的 advice（切面），并转换成 拦截器（执行增强逻辑的执行器）。
     * 逻辑类似 {@link org.springframework.aop.framework.AdvisedSupport#getInterceptorsAndDynamicInterceptionAdvice(java.lang.reflect.Method, java.lang.Class) AdvisedSupport#getInterceptorsAndDynamicInterceptionAdvice}
     *
     * @param method        正在执行的方法
     * @param targetClass   目标类
     * @return  匹配当前方法的拦截器
     */
    private List<MethodInterceptor> getInterceptors(Method method, Class<?> targetClass) {
        List<MethodInterceptor> list = new ArrayList<>(this.advisors.size());
        for (PointcutAdvisor advisor : this.advisors) {
            Pointcut pointcut = advisor.getPointcut();
            if (pointcut.getClassFilter().matches(targetClass) && pointcut.getMethodMatcher().matches(method, targetClass)) {
                Advice advice = advisor.getAdvice();
                if (advice instanceof MethodBeforeAdvice) {
                    list.add(new MethodBeforeAdviceInterceptor((MethodBeforeAdvice) advice));
                }
                else if (advice instanceof AfterReturningAdvice) {
                    list.add(new AfterReturningAdviceInterceptor((AfterReturningAdvice) advice));
                }
                else {
                    throw new RuntimeException("未知的 advice类型");
                }
            }
        }

        return list;
    }

    public List<PointcutAdvisor> getAdvisors() {
        return advisors;
    }

    public void setAdvisors(List<PointcutAdvisor> advisors) {
        this.advisors = advisors;
    }

    public void addAdvisors(PointcutAdvisor advisors) {
        this.advisors.add(advisors);
    }

}
